package com.itjeffrey.autocode.quartz.schedule;

import com.itjeffrey.autocode.anno.SchedulerJob;
import com.itjeffrey.autocode.constant.SysConstant;
import com.itjeffrey.autocode.quartz.bean.JobBean;
import com.itjeffrey.autocode.service.ScheduleJobTbService;
import com.itjeffrey.autocode.util.SpringContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;

/**
 * 自定义任务 ScheduleJob
 * @From: Jeffrey
 * @Date: 2021/4/16
 */
public class ScheduleJob extends QuartzJobBean {

    private static Logger log = LoggerFactory.getLogger(ScheduleJob.class);

    /**
     * 执行任务
     * @param context
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        Object o = context.getMergedJobDataMap().get(JobBean.JOB_KEY);
        log.info("ScheduleJob is executing job, jobBean: {}", o.toString());

        if (o instanceof JobBean){
            JobBean jobBean = (JobBean) o;
            String beanName = jobBean.getBeanName();
            log.info("The name of the class in which the scheduled task runs is {}", beanName);
            if(beanName != null && beanName.length() > 0 && !Character.isWhitespace(beanName.charAt(0))){
                StopWatch stopWatch = new StopWatch(jobBean.getJobId());
                try {
                    stopWatch.start();
                    //get cglib aop proxy obj
                    Object o1 = SpringContextHolder.getBean(beanName);
                    //get real target obj (not proxy obj)
                    Object bean = SpringContextHolder.getTarget(o1);
                    Class<?> aClass = bean.getClass();
                    Method[] declaredMethods = aClass.getDeclaredMethods();
                    boolean flag = false;
                    for (Method declaredMethod : declaredMethods) {
                        String methodName = declaredMethod.getName();
                        //check the annotation(`@SchedulerJob`) on the class or the method and the methodName(`execute`)
                        SchedulerJob classAnno = aClass.getAnnotation(SchedulerJob.class);
                        SchedulerJob methodAnno = declaredMethod.getAnnotation(SchedulerJob.class);
                        if(StringUtils.equals(methodName, SysConstant.SCHEDULE_JOB_METHOD_NAME)
                                && !(classAnno == null && methodAnno == null)
                        ){
                            /**
                             * invoke the target method by spring proxy obj
                             * note: invoking the target method by the real target obj, which is not monitored by AOP
                             */
                            declaredMethod.invoke(o1, jobBean.getJobParams());
                            stopWatch.stop();
                            flag = true;
                            log.info("ScheduleJob execute success! jobId: {}, times: {}ms", jobBean.getJobId(),
                                    stopWatch.getTotalTimeMillis());
                            break;
                        }
                    }
                    if(!flag){
                        Scheduler scheduler = SpringContextHolder.getBean(Scheduler.class);
                        ScheduleContext.pauseJob(scheduler, jobBean.getJobId());
                        //update status `OFF`
                        ScheduleJobTbService scheduleJobTbService = SpringContextHolder.getBean(ScheduleJobTbService.class);
                        scheduleJobTbService.updateStatus(jobBean.getJobId(), SysConstant.SCHEDULE_JOB_STS_OFF);
                        log.warn("ScheduleJob execute pause! Please check the job method that`s name if is execute " +
                                "And add @SchedulerJob annotation on the class or the method that name is execute, " +
                                "jobId: {}", jobBean.getJobId());
                    }
                } catch (Exception e) {
                    log.error("ScheduleJob execute failed", e);
                }
            }
        }

    }
}
